#ifndef PONTO_H
#define PONTO_H

#include <iostream>

#define MIN_DIVISOR 0.0001
#define MIN_DIVISOR_NEG -MIN_DIVISOR

/**
*   @file   Ponto.h
*   @author Marcelo Oliveira da Silva
*   @author Willian Constancio da Silva
*   @date   11/02/2009
*   @todo
*       @li -
*/

/** Template que implementa um ponto num espaco de duas dimensoes,
*   com dimensoes do tipo T.
**/
template<class T>
class Ponto {

    protected:
    /** Componente em x do ponto */
    T x;

    /** Componente em y do ponto */
    T y;

    public:
    /** Construtor padrao. Inicia com um ponto (0,0) */
    Ponto() {
        x = (T) 0;
        y = (T) 0;
    }

    /** Construtor que cria um ponto x,y.
    *   @param  x   a componente em x do ponto
    *   @param  y   a componente em y do ponto
    */
    inline Ponto(T x, T y){
        this->x = x;
        this->y = y;
    }

    /** Construtor de copia
    *   @param  v   o ponto a ser copiado
    */
    inline Ponto( const Ponto<T> &p ){
        x = p.x;
        y = p.y;
    }

    /** Obtem a componente x do ponto
    *   @return a componente x do ponto
    */
    inline T getX(){
        return x;
    }

    /** Obtem a componente y do ponto
    *   @return a componente y do ponto
    */
    inline T getY(){
        return y;
    }

    /** Atribui uma nova componente y ao ponto
    *   @param  y   a nova componente
    */
    inline void setY(T y){
        this->y = y;
        return;
    }

    /** Atribui uma nova componente x ao ponto
    *   @param  x   a nova componente
    */
    inline void setX(T x){
        this->x = x;
        return;
    }

    /** Atribui novas componentes x,y ao ponto
    *   @param  x   a nova componente em x
    *   @param  y   a nova componente em y
    */
    inline void setXY(T x, T y){
        this->x = x;
        this->y = y;
        return;
    }

    /** Sobrecarga do operador << para imprimir o ponto em uma ostream (cout).
    *   @param  out a stream no qual o ponto sera impresso
    *   @param  p   o ponto a ser impresso
    */
    inline friend std::ostream &operator<<(std::ostream &out, Ponto<T> &p) {
        T x, y;
        x = p.getX();
        y = p.getY();
        out << "(" << x << ", " << y << ")";
        return out;
    }

    /** Sobrecarga do operador << para ler um ponto em uma istream (cin).
    *   @param  in  a stream da qual o ponto sera lido
    *   @param  p   o objeto que armazenara o ponto lido
    */
    inline friend std::istream &operator>>(std::istream &in, Ponto<T> &p) {
        T x, y;
        in >> x >> y;
        p.setXY(x,y);
        return in;
    }

    /** Sobrecarga do operador + para a soma de pontos
    *   @param  b ponto a ser somado
    *   @return a soma componente a componente entre os dois pontos
    */
    inline Ponto operator+(const Ponto<T> &b) {
        Ponto<T> aux;
        aux.x = x + b.x;
        aux.y = y + b.y;
        return aux;
    }

    /** Sobrecarga do operador - para a subtracao de pontos
    *   @param  b ponto a ser subtraido
    *   @return a subtracao componente a componente entre os dois pontos
    */
    inline Ponto operator-(const Ponto<T> &b) {
        Ponto<T> aux;
        aux.x = x - b.x;
        aux.y = y - b.y;
        return aux;
    }

    /** Sobrecarga do operador * para a multiplicao de ponto por escalar
    *   @param  b o escalar que multiplicara o ponto
    *   @return a multiplicacao de todas as componentes do ponto pelo escalar
    */
    inline Ponto operator*(const double &b) {
        Ponto<T> aux;
        aux.x = x*b;
        aux.y = y*b;
        return aux;
    }

    /* // Por padrao o C++ ja copia as classes membro a membro
    inline void operator=(const Ponto<T> &b) {
        x = b.x;
        y = b.y;
        return;
    } */

    /** Sobrecarga do operador / para a divisao de ponto por escalar
    *   @param  b o escalar que dividira o ponto
    *   @return a divisao de todas as componentes do ponto pelo escalar
    */
    inline Ponto operator/(const double &b) {
        double b2 = b;
        Ponto<T> aux;

        if( (b < MIN_DIVISOR) && (b>= 0) ) //if ( 0 <= b < MIN_DIVISOR )
            b2 = MIN_DIVISOR;
        if( (b > MIN_DIVISOR_NEG) && (b< 0) ) //if ( -MIN_DIVISOR < b < 0 )
            b2 = MIN_DIVISOR_NEG;

        aux.x = x/b2;
        aux.y = y/b2;
        return aux;
    }


    /** Sobrecarga do operador = para a atribuicao de pontos
    *   @param  b ponto a ser atribuido
    */

    inline void operator=(const Ponto<T> &b) {
        x = b.x;
        y = b.y;
        return;
    }


    /** Sobrecarga do operador += para a soma de pontos
    *   @param  b ponto a ser somado
    *   @return a soma componente a componente entre os dois pontos
    */
    inline void operator+=(const Ponto<T> &b) {
        x = x + b.x;
        y = y + b.y;
        return;
    }

    /** Sobrecarga do operador -= para a subtracao de pontos
    *   @param  b ponto a ser subtraido
    *   @return a subtracao componente a componente entre os dois pontos
    */

    inline void operator-=(const Ponto<T> &b) {
        x = x - b.x;
        y = y - b.y;
        return;
    }

    /** Sobrecarga do operador *= para a multiplicao de ponto por escalar
    *   @param  b o escalar que multiplicara o ponto
    *   @return a multiplicacao de todas as componentes do ponto pelo escalar
    */
    inline void operator*=(const double &b) {
        x = x*b;
        y = y*b;
        return;
    }

    /** Sobrecarga do operador /= para a divisao de ponto por escalar
    *   @param  b o escalar que dividira o ponto
    *   @return a divisao de todas as componentes do ponto pelo escalar
    */
    inline void operator/=(const double &b) {
        double b2 = b;

        if( (b < MIN_DIVISOR) && (b>= 0) ) //if ( 0 <= b < MIN_DIVISOR )
            b2 = MIN_DIVISOR;
        if( (b > MIN_DIVISOR_NEG) && (b< 0) ) //if ( -MIN_DIVISOR < b < 0 )
            b2 = MIN_DIVISOR_NEG;

        x = x/b2;
        y = y/b2;
        return;
    }

    /** Suite de testes */
    static void teste() {
        std::clog << "Construtor padrao...";
        Ponto<T> z; // Testando o construtor padrao
        assert( z.x == 0 );
        assert( z.y == 0 );
        std::clog << " OK!" << std::endl;

        std::clog << "Construtor com x,y...";
        Ponto<T> a(4,3); // Testando o construtor com x,y
        assert( a.x == 4 );
        assert( a.y == 3 );
        std::clog << " OK!" << std::endl;

        std::clog << "Construtor de copia...";
        Ponto<T> b(a); // Testando o construtor de copia
        assert( b.x == 4 );
        assert( b.y == 3 );
        std::clog << " OK!" << std::endl;

        std::clog << "Metodo getX()...";
        assert( b.getX() == b.x ); // Testando o metodo getX()
        std::clog << " OK!" << std::endl;

        std::clog << "Metodo getY()...";
        assert( b.getY() == b.y ); // Testando o metodo getY()
        std::clog << " OK!" << std::endl;

        std::clog << "Metodo setX()...";
        b.setX(6); // Testando o metodo setX()
        assert( b.x == 6 );
        std::clog << " OK!" << std::endl;

        std::clog << "Metodo setY()...";
        b.setY(9); // Testando o metodo setY()
        assert( b.y == 9 );
        std::clog << " OK!" << std::endl;

        std::clog << "Metodo setXY()...";
        b.setXY(14,15); // Testando o metodo setXY()
        assert( b.x == 14 );
        assert( b.y == 15 );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador <<...";
        std::cout << b; // Testando o operador <<
        std::clog << " OK!" << std::endl;

        std::clog << "Operador >>...";
        Ponto<T> c;
        std::cin >> c; // Testando o operador >>
        std::cout << c;
        std::clog << " OK!" << std::endl;

        std::clog << "Operador +...";
        Ponto<T> d;
        d = c + b; // Testando o operador +
        assert ( d.x == ( c.x + b.x ) );
        assert ( d.y == ( c.y + b.y ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador -...";
        d = c - b; // Testando o operador -
        assert ( d.x == ( c.x - b.x ) );
        assert ( d.y == ( c.y - b.y ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador *const...";
        d = b*4; // Testando o operador *
        assert ( d.x == ( b.x * 4 ) );
        assert ( d.y == ( b.y * 4 ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador /const...";
        d = b/4; // Testando o operador /
        assert ( d.x == ( b.x / 4 ) );
        assert ( d.y == ( b.y / 4 ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador +=...";
        d = b; // Testando o operador +=
        b += a;
        assert ( b.x == ( d.x + a.x ) );
        assert ( b.y == ( d.y + a.y ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador -=...";
        d = b; // Testando o operador +=
        b -= a;
        assert ( b.x == ( d.x - a.x ) );
        assert ( b.y == ( d.y - a.y ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador *=...";
        d = b; // Testando o operador *=
        b *= 8;
        assert ( b.x == ( d.x * 8 ) );
        assert ( b.y == ( d.y * 8 ) );
        std::clog << " OK!" << std::endl;

        std::clog << "Operador /=...";
        d = b; // Testando o operador /=
        b /= 8;
        assert ( b.x == ( d.x / (double)8 ) );
        assert ( b.y == ( d.y / (double)8 ) );
        std::clog << " OK!" << std::endl;
    }


};


#endif

